<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    
    <title>python内存管理机制 | 王鹏的技术博客</title>
    
    
        <meta property="og:site_name" content="王鹏的技术博客">
    
    
        <meta property="article:author" content="王 鹏">
    
    
        <link rel="icon" href="/mhexo/img/favicon.ico">
    
    
<link rel="stylesheet" href="/mhexo/css/minireset.min.css">

    
<link rel="stylesheet" href="/mhexo/css/all.min.css">

    
<link rel="stylesheet" href="/mhexo/css/csshake.min.css">

    
<link rel="stylesheet" href="/mhexo/css/hljs/lioshi.css">

    
<link rel="stylesheet" href="/mhexo/css/jquery.fancybox.min.css">

    
<link rel="stylesheet" href="/mhexo/styl/main.css">

    
<script src="/mhexo/js/jquery.min.js"></script>

    
<script src="/mhexo/js/highlight.min.js"></script>

    
<script src="/mhexo/js/jquery.fancybox.min.js"></script>

    
<script src="/mhexo/js/clipboard.min.js"></script>

<meta name="generator" content="Hexo 5.2.0"></head>
<body>
    <header>
  <div class="outer">
    <div class="inner">
      <h1 class="logo-wrap">
        <a>王鹏的技术博客<b><sup>1.1</sup></b></a>
      </h1>
    </div>
    <div class="inner">
      <nav class="main-nav">
        
          <a href="/mhexo/">首页</a>
        
          <a href="/mhexo/archives">归档</a>
        
          <a href="/mhexo/categories">分类</a>
        
          <a href="/mhexo/tags">标签</a>
        
          <a href="/mhexo/friends">友链</a>
        
          <a href="/mhexo/about">关于</a>
        
          <a target="_blank" rel="noopener" href="https://www.baidu.com">百度</a>
        
      </nav>
    </div>
  </div>
</header>
    <div class="content">
        <section class="outer">
    <article>
        <div class="article-title">
    <h2>
        <a href="/mhexo/2020/12/15/python%E6%B7%B1%E5%85%A5%E4%B9%8Bpython%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6%EF%BC%88%E9%87%8D%E7%82%B9%EF%BC%89/" class="shake shake-little" title="python内存管理机制">
            
            python内存管理机制
        </a>
    </h2>
    <div class="meta_box">
    
        
        
            
                
        
        <div class="meta meta_auth">
            <img src="/mhexo/img/default.png" alt="head" />
            <p>anonymous</p>
        </div>
    
        
        
        <div class="meta meta_date">
            <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
            <p>发布于：2020年12月15日</p>
        </div>
    
        <div class="meta meta_update">
            <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
            <p>更新于：2020年12月15日</p>
        </div>
    </div>
    

</div>

        <hr>
        <div class="article-entry">
            
            
            
            <h1 id="python深入之python内存管理机制（重点）"><a href="#python深入之python内存管理机制（重点）" class="headerlink" title="python深入之python内存管理机制（重点）"></a>python深入之python内存管理机制（重点）</h1><p>关于python的存储问题</p>
<p>（1）由于python中万物皆对象，所以python的存储问题是对象的存储问题，并且对于每个对象，python会分配一块内存空间去存储它</p>
<p>（2）对于整数和短小的字符等，python会执行缓存机制，即将这些对象进行缓存，不会为相同的对象分配多个内存空间</p>
<p>（3）容器对象，如列表、元组、字典等，存储的其他对象，仅仅是其他对象的引用，即地址，并不是这些对象本身</p>
<p>关于引用计数器</p>
<p>（1）一个对象会记录着引用自己的对象的个数，每增加一个引用，个数加一，每减少一个引用，个数减一</p>
<p>（2）查看引用对象个数的方法：导入sys模块，使用模块中的getrefcount(对象)方法，由于这里也是一个引用，故输出的结果多1</p>
<p>（3）增加引用个数的情况：1.对象被创建p = Person()，增加1；2.对象被引用p1 = p，增加1；3.对象被当作参数传入函数func(object)，增加2，原因是函数中有两个属性在引用该对象；4.对象存储到容器对象中l = [p]，增加1</p>
<p>（4）减少引用个数的情况：1.对象的别名被销毁del p，减少1；2.对象的别名被赋予其他对象，减少1；3.对象离开自己的作用域，如getrefcount(对象)方法，每次用完后，其对对象的那个引用就会被销毁，减少1；4.对象从容器对象中删除，或者容器对象被销毁，减少1</p>
<p>（5）引用计数器用法：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">import sys</span><br><span class="line">class Person(object):</span><br><span class="line">    pass</span><br><span class="line">p &#x3D; Person()</span><br><span class="line">p1 &#x3D; p</span><br><span class="line">print(sys.getrefcount(p))</span><br><span class="line">p2 &#x3D; p1</span><br><span class="line">print(sys.getrefcount(p))</span><br><span class="line">p3 &#x3D; p2</span><br><span class="line">print(sys.getrefcount(p))</span><br><span class="line">del p1</span><br><span class="line">print(sys.getrefcount(p))</span><br></pre></td></tr></table></figure>

<p>多一个引用，结果加1，销毁一个引用，结果减少1</p>
<p>（6）引用计数器机制：利用引用计数器方法，在检测到对象引用个数为0时，对普通的对象进行释放内存的机制</p>
<p>关于循环引用问题</p>
<p>（1）循环引用即对象之间进行相互引用，出现循环引用后，利用上述引用计数机制无法对循环引用中的对象进行释放空间，这就是循环引用问题</p>
<p>（2）循环引用形式：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class Person(object):</span><br><span class="line">    pass</span><br><span class="line">class Dog(object):</span><br><span class="line">    pass</span><br><span class="line">p &#x3D; Person()</span><br><span class="line">d &#x3D; Dog()</span><br><span class="line">p.pet &#x3D; d</span><br><span class="line">d.master &#x3D; p</span><br></pre></td></tr></table></figure>

<p>即对象p中的属性引用d，而对象d中属性同时来引用p，从而造成仅仅删除p和d对象，也无法释放其内存空间，因为他们依然在被引用。深入解释就是，循环引用后，p和d被引用个数为2，删除p和d对象后，两者被引用个数变为1，并不是0，而python只有在检查到一个对象的被引用个数为0时，才会自动释放其内存，所以这里无法释放p和d的内存空间</p>
<p>关于垃圾回收（底层层面–原理）</p>
<p>（1）垃圾回收的作用：从经过引用计数器机制后还没有被释放掉内存的对象中，找到循环引用对象，并释放掉其内存</p>
<p>（2）垃圾回收检测流程：</p>
<p>一.任何找到循环引用并释放内存：1.收集所有容器对象（循环引用只针对于容器对象，其他对象不会产生循环引用），使用双向链表（可以看作一个集合）对这些对象进行引用；2.针对每一个容器对象，使用变量gc_refs来记录当前对应的应用个数；3.对于每个容器对象，找到其正在引用的其他容器对象，并将这个被引用的容器对象引用计数减去1；4.经过步骤3后，检查所有容器对象的引用计数，若为0，则证明该容器对象是由于循环引用存活下来的，并对其进行销毁</p>
<p>二.如何提升查找循环引用过程的性能：由一可知，循环引用查找和销毁过程非常繁琐，要分别处理每一个容器对象，所以python考虑一种改善性能的做法，即分代回收。首先是一个假设–如果一个对象被检测了10次还没有被销毁，就减少对其的检测频率；基于这个假设，提出一套机制，即分代回收机制。</p>
<p>通过这个机制，循环引用处理过程就会得到很大的性能提升</p>
<p>关于垃圾回收时机（应用层面–重点）</p>
<p>（1）自动回收：</p>
<p>（2）手动回收：这里要使用gc模块中的collect()方法，使得执行这个方法时执行分代回收机制</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">import objgraph</span><br><span class="line">import gc</span><br><span class="line">import sys</span><br><span class="line">class Person(object):</span><br><span class="line">    pass</span><br><span class="line">class Dog(object):</span><br><span class="line">    pass</span><br><span class="line">p &#x3D; Person()</span><br><span class="line">d &#x3D; Dog()</span><br><span class="line">p.pet &#x3D; d</span><br><span class="line">d.master &#x3D; p</span><br><span class="line">del p</span><br><span class="line">del d</span><br><span class="line">gc.collect()</span><br><span class="line">print(objgraph.count(&quot;Person&quot;))</span><br><span class="line">print(objgraph.count(&quot;Dog&quot;))</span><br></pre></td></tr></table></figure>

<p>其中objgraph模块的count()方法是记录当前类产生的实例对象的个数</p>
<p>关于内存管理机制的总结（重点）</p>
<p>综上所述，python的内存管理机制就是引用计数器机制和垃圾回收机制的混合机制</p>

        </div>
        <div class="article-copyright">
            
    <blockquote>
        <p>
            版权声明：本文为「子曰无衣」的原创文章，博客内容遵循 署名-非商业性使用-相同方式共享 协议。<br>本文永久链接是：https://wangpdizhi.gitee.io/mhexo/2020/12/15/python%E6%B7%B1%E5%85%A5%E4%B9%8Bpython%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6%EF%BC%88%E9%87%8D%E7%82%B9%EF%BC%89/
        </p>
    </blockquote>


        </div>
    </article>
    

    <section id="toc-div" >
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#python%E6%B7%B1%E5%85%A5%E4%B9%8Bpython%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6%EF%BC%88%E9%87%8D%E7%82%B9%EF%BC%89"><span class="toc-text">python深入之python内存管理机制（重点）</span></a></li></ol>
    </section>
    <section id="gohome" style="display: none;"><a>顶</a></section>

    <script>
        function get_top_by_link(link){
            var hnid = "#" + $(link).attr("data");
            if ($(hnid).length > 0){
                return $(hnid).offset().top;
            }else{
                return 0;
            }
        }
        //go to hn
        function gotohn(link){
            $("html,body").animate({scrollTop: get_top_by_link(link) }, 300);
        }
        //页面滚动
        function update(){
            var scrollH = $(window).scrollTop();
            if($(".toc-link")){
                $(".toc-link").each(function(i,link){
                    var mdHeight = get_top_by_link(link);
                    if(mdHeight <= scrollH + 40){
                        //高亮导航菜单
                        $('.toc-link').removeClass('on');
                        $(link).addClass('on');
                    }
                });
            }
            //返回顶部显隐
            if(scrollH < 200){
                $("#gohome").css("display","none");
            }else{
                $("#gohome").css("display","block");
            }
        }
        $(function(){
            //修复部分锚点从属关系
            if($("#toc-div >li").length > 0){
                $("#toc-div >li").appendTo($("#toc-div >ol:first"));
            }
            //返回顶部
            $('#gohome').click(function(){
                $("html,body").animate({scrollTop: 0}, 300);
                return false;
            })
            //遍历锚点
            $(".toc-link").each(function(i,link){
                $(link).attr("data",$(link).attr('href').substring(1));
                $(link).attr("href","javascript:void(0);");
                $(link).attr("onclick","gotohn(this);");
            })
            //绑定滚动事件
            $(window).bind('scroll', update);
            //初始化toc
            var first_toc = $(".toc-link")[0];
            var first_scroll = get_top_by_link(first_toc);
            var window_scroll = $(window).scrollTop();
            if(window_scroll <= first_scroll){
                $(first_toc).addClass('on');
            }
        })
    </script>

</section>
    </div>
    <footer>
    <div class="outer">
        <div class="inner">
            Powered by <a href="http://prowiki.demopage.icu/" target="_blank">ProWiki</a>
            &copy;2020 子曰无衣<br>
            <a target="_blank" rel="noopener" href="http://beian.miit.gov.cn/">冀ICP备00000000号</a>
        </div>
    </div>
</footer>

<script src="/mhexo/js/custom.js"></script>


    <script>onload_content();</script>

</body>
</html>
